// 服务器端代码
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <winsock2.h>
#include <algorithm>
#include <unistd.h>
#pragma comment(lib, "ws2_32.lib")


const int MAX_BUFFER_SIZE =1024;
const int MAX_CLIENTS = 10;
struct client{
    client(SOCKET a,std::vector<client*> *b,sockaddr_in c):clientSocket(a),friends(b),addr(c){};
    bool is_online=true;
    SOCKET clientSocket;
    sockaddr_in addr;
    std::vector<client*> *friends;
};
std::vector<client> clientSockets;
std::mutex mtx;
std::condition_variable cv;
void sendToAllClients(const char* message, SOCKET senderSocket) {
    std::unique_lock<std::mutex> lock(mtx);
    for (auto clientSocket : clientSockets) {
        if (clientSocket.clientSocket != senderSocket) {
            send(clientSocket.clientSocket, message, strlen(message), 0);
        }
    }
}
bool FriendRequest(client *requstSocket,std::string targetIp)
{
    auto it = std::find_if(clientSockets.begin(), clientSockets.end(),[&](const client &c)
                 {
        return inet_ntoa(c.addr.sin_addr) == targetIp.c_str();
                 }
    );
    std::string friendMessage;
    std::string ip = inet_ntoa(requstSocket->addr.sin_addr);
    friendMessage = "A friend Request From :" + ip + "please enter Y or N to choose to accept it or not\n";
    send(it->clientSocket,friendMessage.c_str(),friendMessage.size(),0);
    char buffer[2];
    recv(it->clientSocket,buffer,1,0);
    if(buffer[0]=='Y'||buffer[0]=='y')
    {
        it->friends->emplace_back(requstSocket);
        requstSocket->friends->emplace_back(&*it);
        std::string success;
        success = "Friend request has been accepted!\n";
        send(requstSocket->clientSocket,success.c_str(),success.size(),0);
        return true;
    }
    else
    {
        std::string failed;
        failed = "Friend request has been denied!\n";
        send(requstSocket->clientSocket,failed.c_str(),failed.size(),0);
        return false;
    }
}
void sendToSpecificClient(const char* message, SOCKET targetSocket){
    std::unique_lock<std::mutex> lock(mtx);
    for (auto clientSocket : clientSockets) {
        if (clientSocket.clientSocket == targetSocket) {
            send(clientSocket.clientSocket, message, strlen(message), 0);
        }
    }
}
void SendFriendListTo(client *requstSocket)
{
    if(requstSocket->friends->empty())
    {
        std::string message ="No Friends in your list!\n";
        sendToSpecificClient(message.c_str(),requstSocket->clientSocket);
        return;
    }
    for(auto Friend:*requstSocket->friends)
    {
        std::string ip = inet_ntoa(Friend->addr.sin_addr);
        std::string message = ip+"  "+(Friend->is_online?"is online\n":"is offline\n");
        sendToSpecificClient(message.c_str(),requstSocket->clientSocket);
    }
}
void SendOnlineListTo(client *requstSocket)
{
    for(auto client:clientSockets)
    {
        if (client.is_online) {
            std::string ip = inet_ntoa(client.addr.sin_addr);
            std::string message = ip+"  ""is online\n";
            sendToSpecificClient(message.c_str(),requstSocket->clientSocket);
        } else {
            continue;
        }
    }
}
auto FindClient(std::string targetIp)
{
    return  std::find_if(clientSockets.begin(), clientSockets.end(),[&](const client &c)
                                      {
                                          return inet_ntoa(c.addr.sin_addr) == targetIp.c_str();
                                      }
    );
}
void BuildChat(client chat1, client chat2)
{
    while(true)
    {
        char buffer[MAX_BUFFER_SIZE];
        int MessageLength = recv(chat1.clientSocket,buffer,sizeof(buffer),0);
        buffer[MessageLength]='\0';
        send(chat2.clientSocket,buffer,MessageLength,0);
    }
}
void start(client *chat1, client *chat2)
{
    std::thread Thread_one = std::thread(BuildChat,*chat1, *chat2);
    std::thread Thread_two = std::thread(BuildChat,*chat2, *chat1);
    Thread_one.detach();
    Thread_one.detach();
}
void clientThread(client *clientSocket,struct sockaddr_in* clientAddr) {
    char buffer[1024];
    while (true) {
        memset(buffer, 0, sizeof(buffer));
        int bytesRead = recv(clientSocket->clientSocket, buffer, sizeof(buffer) - 1, 0);
        buffer[bytesRead]='\0';
        std::string temp=buffer;
        if(temp == "logout")
        {
            std::string temp = "Client";
            inet_ntoa(clientAddr->sin_addr);
            temp = temp + inet_ntoa(clientAddr->sin_addr)+" has logout";
            clientSocket->is_online=false;
            sendToAllClients(temp.c_str(), clientSocket->clientSocket);
            break;
        }
        if(temp == "friend_list")
        {
            SendFriendListTo(clientSocket);
            continue;
        }
        if(temp == "online_people")
        {
            SendOnlineListTo(clientSocket);
            continue;
        }
        if(temp == "send friend request")
        {
            std::string message = "please enter target ip:\n";
            sendToSpecificClient(message.c_str(), clientSocket->clientSocket);
            std::string ip;
            char ip_char[20];
            int length = recv(clientSocket->clientSocket,ip_char,sizeof(ip_char),0);
            ip_char[length]='\0';
            ip = ip_char;
            FriendRequest(clientSocket,ip);
        }
        if(temp == "chat to:")
        {
            SendFriendListTo(clientSocket);
            std::string ip;
            char ip_char[20];
            int length = recv(clientSocket->clientSocket,ip_char,sizeof(ip_char),0);
            ip_char[length]='\0';
            ip = ip_char;
            auto target = FindClient(ip);
            //target==clientSockets.end() ? std::cout<<"chat target doesn't exit!\n" : BuildChat(*clientSocket,*target);
            if (target == clientSockets.end()) {
                std::string message = "chat target doesn't exist!\n";
                sendToSpecificClient(message.c_str(), target->clientSocket);
                continue;
            } else {
                start(clientSocket,(&*target));\
                continue;
            }
        }
        if(bytesRead <= 0) {
            std::unique_lock<std::mutex> lock(mtx);
            clientSockets.erase(std::remove_if(clientSockets.begin(),
                               clientSockets.end(),
                               [&](const client& c)
                               {
                                return c.clientSocket == clientSocket->clientSocket;
                               }), clientSockets.end());
            lock.unlock();
            cv.notify_all();
            break;
        }
        //sendToAllClients(buffer, *clientSocket);
    }
    closesocket(clientSocket->clientSocket);
}
int main() {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "Failed to initialize winsock" << std::endl;
        return -1;
    }
    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == INVALID_SOCKET) {
        std::cerr << "Failed to create server socket." << std::endl;
        WSACleanup();
        return -1;
    }
    sockaddr_in serverAddr{};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8888);
    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cerr << "Failed to bind server socket." << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return -1;
    }
    if (listen(serverSocket, MAX_CLIENTS) == SOCKET_ERROR) {
        std::cerr << "Failed to listen on server socket." << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return -1;
    }
    std::cout << "Server started. Waiting for connections..." << std::endl;
    sockaddr_in clientAddr{};
    int clientAddrLen = sizeof(clientAddr);




    while (true) {
        SOCKET clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
        if (clientSocket == INVALID_SOCKET) {
            std::cerr << "Failed to accept client connection." << std::endl;
            continue;
        }
        std::unique_lock<std::mutex> lock(mtx);
        std::vector<client*> friends;
        client temp(clientSocket,&friends,clientAddr);
        clientSockets.emplace_back(temp);
        lock.unlock();
        cv.notify_all();
        std::thread clientThread_work(&clientThread, &temp,&clientAddr);
        clientThread_work.detach();
        std::cout << "New client connected. Client IP: " << inet_ntoa(clientAddr.sin_addr) << std::endl;
    }
    closesocket(serverSocket);
    WSACleanup();
    return 0;
}